package service;

import entity.User;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.fileupload.FileItem;
import utils.Common;
import utils.RequestFileDispatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class UserServiceImp extends dao.UserDAOImp implements UserService {

    private static UserServiceImp userServiceImp      = null;

    public static UserServiceImp getUserServiceImp(FileStoreService fileStoreService) {
        if(userServiceImp == null) return userServiceImp = new UserServiceImp(fileStoreService);
        else return userServiceImp;
    }
    //////////////////////////////////////////////////////////////////////////
    private FileStoreService fileStoreService;
    private ConcurrentHashMap<HttpSession, User> loginUser;
    private ConcurrentHashMap<User, HttpSession> sessionIndex;
    //private PriorityBlockingQueue<Long, HttpSession> sessionTimeQueue;
    //////////////////////////////////////////////////////////////////////////
    @Override
    public User register(String uname, String unick, String upass) {
        return super.register(uname, unick, DigestUtils.md5Hex(upass));
    }

    @Override
    public User getUserInfo(HttpSession session) {
        return loginUser.get(session);
    }

    @Override
    public User registerLogin(HttpSession session, String uname, String unick, String upass) {
        User u = null;
        if((u = register(uname, unick, upass)) != null) {
            loginUser.put(session, u);
            sessionIndex.put(u, session);
        }
        return u;
    }

    @Override
    public User isLoggedIn(HttpSession session) {
        return loginUser.get(session);
    }

    /**
     * 用户登录实现
     * @param session 会话id
     * @param username 用户名
     * @param pass256 登录口令经过B端SHA256加密后传输到服务器的副本
     * @return
     */
    @Override
    public User login(HttpSession session, String username, String pass256) {
        if(session == null || Common.isStringEmpty(username) ||
                Common.isStringEmpty(pass256)) return null;

        //加密口令
        String passMD5 = DigestUtils.md5Hex(pass256);

        User u = isLoggedIn(session);
        if(u != null) { //当前会话已登录
            //System.out.print("当前会话(" + session.getId() + "已登录");
            if(passMD5.equals(u.getPasswordHash())) {
                return u;
            } else {
                logOut(session);
                return null;
            }
        } else {
            u = new User();
            u.setName(username);
            HttpSession s = sessionIndex.get(u);

            if(s != null) { //当前帐号已在其它会话登录
                //System.out.println("当前用户(" + username + ")已在其它会话(" + s.getId() + ")登录");
                u = loginUser.get(s); //在内存中查找已登录的用户信息
                if(u != null) { //缓存命中
                    //System.out.print("\t已在内存中查找到用户 " + username + " 的数据副本，无需查找数据库");
                    if(passMD5.equals(u.getPasswordHash())) {
                        loginUser.remove(s); // 踢出
                        loginUser.put(session, u);
                        sessionIndex.put(u, session);
                        s.invalidate(); // 销毁踢出的会话
                        //System.out.println("已登录的旧会话将被登出，登录成功！");
                        return u;
                    } else {
                        //System.out.println("，但此次验证口令不正确，登录失败。");
                        return null;
                    }
                } //帐号已登录但未命中缓存
            } //帐号未登录

            //从数据库查询用户信息
            u = query(username);
            if(u == null) return null;

            //验证口令
            if(!u.getPasswordHash().equals(passMD5)) return null;

            sessionIndex.put(u, session);
            loginUser.put(session, u);

            //System.out.println(username + " logged in with session " + session.getId());

            return u;
        }
    }

    @Override
    public String putHeadPic(HttpServletRequest req) {
        try {
            //使用帮助类从浏览器接收表单数据（包括文件和非文件数据）
            RequestFileDispatcher data = new RequestFileDispatcher(req);
            //根据表单控件name属性，获取对应的文件数据：getFileItem
            FileItem pic = data.getFileItem("file");

            User u = getUserInfo(req.getSession());

            //存储头像
            if(fileStoreService.store(
                    req, pic, "/pictures", u.getId().toString()
            ) == null) return null;

            //更新数据库
            User newUpdate = new User();
            newUpdate.setHeadPic(u.getId().toString());
            update(u.getId(), null, newUpdate);

            //更新缓存
            u.setHeadPic(u.getId().toString());
            return u.getHeadPic();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public String changeNickname(HttpSession session, String nickname) {
        if(session == null || Common.isStringEmpty(nickname)) return null;
        //从登录列表中搜索会话
        User u = loginUser.get(session);
        if(u == null) return null;
        //更新数据库
        User updateData = new User(null, null,  nickname, null,
                null, null, null, null,
                null, null, null
        );
        if(update(u.getId(), null, updateData)) {
            //更新缓存
            u.setNick(nickname);
            return nickname;
        }
        return null;
    }

    @Override
    public String changePassword(HttpSession session, /*String oldPass256,*/ String newPass256) {
        if(session == null || /*Common.isStringEmpty(oldPass256) ||*/
                Common.isStringEmpty(newPass256)) return null;

        //从登录列表中搜索会话
        User u = loginUser.get(session);
        if(u == null) return null;

        //if(oldPass256.equals(newPass256)) return oldPass256;

        //加密口令
        //oldPass256 = DigestUtils.md5Hex(oldPass256);
        newPass256 = DigestUtils.md5Hex(newPass256);

        //验证口令
        //if(!u.getPasswordHash().equals(oldPass256)) return null;

        //更新数据库
        User updateData = new User(null, null,  null, newPass256,
                null, null, null, null,
                null, null, null
        );
        if(update(u.getId(), null, updateData)) {
            //更新缓存
            u.setPasswordHash(newPass256);
            return newPass256;
        }
        return null;
    }

    @Override
    public boolean logOut(HttpSession session) {
        if(session == null) return false;
        User u = loginUser.remove(session);
        if(u != null) {
            HttpSession s = sessionIndex.remove(u);
            s.invalidate();
            System.out.println("User: " + u.getName() + " logged out.");
        }
        return true;
    }

    @Override
    public void commitGameResult(User u, UserGameResult result, long score) {
        if(u == null) return;

        long newScore = u.getScore() + score;
        //更新数据库
        User updateData = new User(
            null, null,  null, null,
            null, null, null, null,
                newScore, null, null
        );

        int x;

        switch (result) {
            case win:  updateData.setWin(x = (u.getWin() + 1));   break;
            case lost: updateData.setLost(x = (u.getLost() + 1)); break;
            case run:  updateData.setRun(x = (u.getRun() + 1));   break;
            case draw: x = 0; break;
            default: return;
        }

        if(update(u.getId(), null, updateData)) {
            //更新缓存
            switch (result) {
                case win:  u.setWin(x);  break;
                case lost: u.setLost(x); break;
                case run:  u.setRun(x);  break;
                case draw: break;
            }
            u.setScore(newScore);
        }
    }

    @Override
    public void joinRank(User u, boolean join) {
        if(u == null) return;

        //更新数据库
        User updateData = new User(
                null, null,  null, null,
                null, null, null, null,
                null, join? 1: 0, null
        );

        if(update(u.getId(), null, updateData)) {
            //更新缓存
            u.setJoinRank(join? 1: 0);
        }
    }

    @Override
    public List<User> searchByNick(String unick) {
        return queryByNick(unick);
    }

    private UserServiceImp(FileStoreService fileStoreService) {
        super();
        this.fileStoreService = fileStoreService;
        this.loginUser = new ConcurrentHashMap<>();
        this.sessionIndex = new ConcurrentHashMap<>();
    }
}
